package com.credithc.eagleeye.plugins.scheduling;
import com.credithc.eagleeye.agent.factory.EagleEyeAgentFactory;
import com.credithc.eagleeye.agent.util.HostUtil;
import com.credithc.eagleeye.constants.RpcTypeEnum;
import com.credithc.eagleeye.exception.EagleEyeIgnoreException;
import com.credithc.eagleeye.plugins.util.LogUtil;
import com.credithc.eagleeye.thread.EagleEyeThreadContext;
import com.credithc.eagleeye.thread.TraceInfo;
import com.credithc.eagleeye.util.MixUtil;
import com.credithc.eagleeye.util.ThrowableToStringArray;
import com.credithc.eagleeye.vo.RpcTraceInfoVo;
import org.apache.commons.lang3.time.FastDateFormat;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;

import java.util.Date;
import java.util.regex.Pattern;
/**
 * 描 述: <br/>
 * 作 者: zhangjin<br/>
 * 创 建：2019年10月24日<br/>
 * 版 本：v1.0.0<br>
 * <p>
 * 历 史: (版本) 作者 时间 注释 <br/>
 */
@SuppressWarnings("DuplicatedCode")
@Aspect
public class EagleEyeSchedulingAspect {
    private transient static final Logger logger = LoggerFactory.getLogger(EagleEyeSchedulingAspect.class);
    private final Pattern skipPattern=Pattern.compile("org.springframework.cloud.netflix.hystrix.stream.HystrixStreamTask");
    public static final FastDateFormat DATETIME_TIME_FORMAT = FastDateFormat.getInstance("yyyy-MM-dd HH:mm:ss");

    public EagleEyeSchedulingAspect() {
        System.out.println("EagleEyeSchedulingAspect init..");
    }

    @Around("execution (@org.springframework.scheduling.annotation.Scheduled  * *.*(..))")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        logger.debug("EagleEyeSchedulingAspect around start...");

        if (this.skipPattern.matcher(joinPoint.getTarget().getClass().getName()).matches()) {
            return joinPoint.proceed();
        }
        Object resultObject = null;
        RpcTraceInfoVo rpcTraceInfoVo = null;
        //执行调用
        long beginTime = System.currentTimeMillis();
        try {
            TraceInfo traceInfo = new TraceInfo();
            //初始化调用链数据
            rpcTraceInfoVo = initRpcTraceInfoVo(traceInfo, joinPoint);
            //放入EagleEyeThreadLocal
            initEagleEyeThreadContext(traceInfo);

            beginTime = System.currentTimeMillis();
            resultObject = joinPoint.proceed();
            rpcTraceInfoVo.setRunTime(System.currentTimeMillis()-beginTime);
            rpcTraceInfoVo.setResult("OK");
            //返回结果
            rpcTraceInfoVo.setResponseJson(String.format("job end:%s,elapsed time:%sms",DATETIME_TIME_FORMAT.format(new Date()),rpcTraceInfoVo.getRunTime()));
        }catch(EagleEyeIgnoreException ex){
            rpcTraceInfoVo.setResult("OK");
        }catch(Exception ex){
            //ERROR
            rpcTraceInfoVo.setRunTime(System.currentTimeMillis()-beginTime);
            rpcTraceInfoVo.setResult("ERROR");
            //异常的结果
            String traceStr[] = ThrowableToStringArray.convert(ex);
            StringBuilder builder = new StringBuilder();
            for(String trace : traceStr) {
                builder.append(trace);
                builder.append("\n");
            }
            rpcTraceInfoVo.setResponseJson(builder.toString());
            throw ex;
        }finally {
            EagleEyeThreadContext.removeTraceInfo();
            MDC.remove("traceId");
            MDC.remove("rpcId");
            MDC.remove("clientIp");
            EagleEyeAgentFactory.getInstance().send2EagleEye(rpcTraceInfoVo);
            LogUtil.printSplunkLog(rpcTraceInfoVo);
        }

        return resultObject;
    }

    private RpcTraceInfoVo initRpcTraceInfoVo(TraceInfo traceInfo,ProceedingJoinPoint joinPoint) {
        RpcTraceInfoVo rpcTraceInfoVo = new RpcTraceInfoVo();

        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        Object target = joinPoint.getTarget();
        String methodName = methodSignature.getMethod().getName();
        String serviceName = target.getClass().getSimpleName();
        Date date = new Date();

        rpcTraceInfoVo.setRequestDateTime(MixUtil.ISO_DATETIME_TIME_ZONE_FORMAT_WITH_MILLIS.format(date));
        rpcTraceInfoVo.setTraceId(traceInfo.getTraceId());
        rpcTraceInfoVo.setRpcId(traceInfo.getRpcId());
        rpcTraceInfoVo.setRpcType(RpcTypeEnum.TASK.getRpcName());
        rpcTraceInfoVo.setServiceCategory("feign");
        rpcTraceInfoVo.setServiceName(serviceName);
        rpcTraceInfoVo.setMethodName(methodName);
        rpcTraceInfoVo.setClientHost(HostUtil.getIp());
        rpcTraceInfoVo.setServerHost(HostUtil.getIp());
        rpcTraceInfoVo.setRequestJson("job start:"+DATETIME_TIME_FORMAT.format(date));
        return rpcTraceInfoVo;
    }

    private void initEagleEyeThreadContext(TraceInfo traceInfo) {
        //子线程增加层级
        traceInfo.addHierarchy();
        EagleEyeThreadContext.init();
        EagleEyeThreadContext.putTraceInfo(traceInfo);
        MDC.put("traceId", traceInfo.getTraceId());
        MDC.put("rpcId", traceInfo.getRpcId());
        MDC.put("clientIp", HostUtil.getIp());
    }
}
